#include "Precomp.h"
#include "Entity.h"
#include "ResourceManager.h"
#include "Map.h"
#include "Renderer.h"
#include "SQGame.h"
#include <sstream>

Entity::Entity(EntityType type)
	:shape(0),type(type),visible(true),currentMap(0),debug(false)
{
}

Entity::~Entity()
{
}

void Entity::init()
{	
	currentLayer=Map::defaultMap->getLayerByIndex(0);
	gotoMap(Map::defaultMap);
}

void Entity::remove()
{
	currentMap->removeEntity(this);
	currentLayer->removeShape(this);
	delete this;//bye bye everyone
}

Entity* Entity::clone() const
{
	SquirrelObject info=SquirrelVM::CreateTable();
	serialize(info);
	SquirrelObject instance;
	//int top=sq_gettop(vm);
	HSQUIRRELVM vm=SquirrelVM::GetVMPtr();
	SquirrelObject Class=SquirrelFunction<SquirrelObject>(m_obj,L"getclass")();
	sq_pushobject(vm,Class);
	sq_pushroottable(vm);		
	if(SQ_SUCCEEDED(sq_call(vm,1,true,true)))//create a new instance
	{
		instance.AttachToStackObject(-1);
		sq_pop(vm,2);//pop the instance and the class
		Entity* newEntity=instance.Get<Entity*>();
		newEntity->deserialize(info);//copy information
		return newEntity;
	}
	else
	{
		sq_pop(vm,1);//pop the class
		return NULL;
	}
}

bool Entity::getVisible() const
{
	return visible;
}

void Entity::setVisible(bool val)
{
	visible=val;
}


void Entity::setPosition(sf::Vector2f& pos)
{
	shape->SetPosition(pos);
}

sf::Vector2f Entity::getPosition() const
{
	return shape->GetPosition();
}

float Entity::getX() const
{
	return getPosition().x;
}

float Entity::getY() const
{
	return getPosition().y;
}

void Entity::setX(float x)
{
	shape->SetX(x);
}

void Entity::setY(float y)
{
	shape->SetY(y);
}

sf::Vector2f Entity::getCenter() const
{
	return shape->GetCenter();
}

void Entity::setCenter(sf::Vector2f& pos)
{
	shape->SetCenter(pos);
	updateBoundingBox();
}

float Entity::getRotation() const
{
	return shape->GetRotation();
}

void Entity::setRotation(float rotation)
{
	return shape->SetRotation(rotation);
}

void Entity::update()
{
	if(overrided(L"update",_update))
	{
		SquirrelVM::BeginCall(_update,m_obj);
		SquirrelVM::EndCall();
	}
}

void Entity::serialize(SquirrelObject& out) const
{
	out.SetValue(L"x",shape->GetPosition().x);
	out.SetValue(L"y",shape->GetPosition().y);
	out.SetValue(L"xoffset",shape->GetCenter().x);
	out.SetValue(L"yoffset",shape->GetCenter().y);
	out.SetValue(L"rotation",shape->GetRotation());
	out.SetValue(L"layer",currentLayer->getName());

	if(m_obj.IsNull()) return;
	//get the class of this instance
	SquirrelObject Class=SquirrelFunction<SquirrelObject>(m_obj,L"getclass")();
	SquirrelObject attribute=Class.GetAttributes();//get the metadata
	SquirrelObject classname=attribute.GetValue(L"__classname");
	if(classname.GetType()==OT_STRING)
	{
		out.SetValue(L"type",classname.ToString());
	}
	else
		return;//do not serialize this object
	if(overrided(L"onSave",_serialize))
	{
		SquirrelFunction<void> f(m_obj,_serialize);
		f(out);
	}	
}

void Entity::deserialize(SquirrelObject& in)
{
	if(m_obj.IsNull()) return;
	if(overrided(L"onLoad",_deserialize))
	{
		SquirrelFunction<void> f(m_obj,_deserialize);
		f(in);
	}
	setPosition(sf::Vector2f(in.GetFloat(L"x"),in.GetFloat(L"y")));
	setRotation(in.GetFloat(L"rotation"));
	setCenter(sf::Vector2f(in.GetFloat(L"xoffset"),in.GetFloat(L"yoffset")));
	switchLayer(currentMap->getLayerByName(in.GetString(L"layer")));
}

void Entity::updateBoundingBox()
{
	switch(type)
	{
		case TEXT:
			size.x=((sf::String*)shape)->GetRect().GetWidth();
			size.y=((sf::String*)shape)->GetRect().GetHeight();
		break;
		case STATIC:
		case ANIMATED:
		case TILE:
			sf::IntRect intRect=((sf::Sprite*)shape)->GetSubRect();
			size.x=(float)intRect.GetWidth();
			size.y=(float)intRect.GetHeight();
		break;
	}
}

void Entity::gotoMap(Map *map)
{
	if(map==NULL)
	{
		throw SquirrelError(L"Cannot go to null map");
		return;
	}
	if(currentMap)
		currentMap->removeEntity(this);
	currentMap=map;
	currentMap->addEntity(this);
	switchLayer(currentMap->getLayerByName(currentLayer->getName()));
}

void Entity::switchLayer(Layer* layer)
{
	if(currentLayer)
		currentLayer->removeShape(this);
	currentLayer=layer;
	currentLayer->addShape(this);
}

const SQChar* Entity::getCurrentLayerName() const
{
	return currentLayer->getName();
}